"""

System module. 

- System class
- Creates a System instance called "na.vala"

"""

from PyQt4.QtCore import *
import logging
import sys
import threading
from misc import misc
import configs as con

class System(QObject):
    """Navala system
    - Contains all System-level variables
    - Contains System startup sequence.

    The Master system will:
    - Connect to the DB locally
    - start DBServer, ProcessMonitor, RealtimeManager threads
    Slave system will:
    - Connect to the DB via Pyro4 Proxy

    """
    ## Setup before starting
    # Whether database should be accessed locally instead of by Proxy
    flagLocalDB = False
    # Whether to start a thread to serve the db variable as a Pyro daemon.
    # If set, the DB will be accessed locally regardless of flagLocalDB.
    flagDBServer = False
    # Whether or not to call self.rtm.Start() (start the Realtime Manager)
    # upon startup. Master only
    flagStartRTM = True
    # Whether to act as a master system. See class documentation for details
    flagMaster = False
    
    ## *read-only* variables
    # database.Database or corresponding proxy
    db = None
    # database.DBServer thread
    dbserver = None
    # Store.store
    store = None
    # ProcessMonitor
    pm = None
    # Realtime manager
    rtm = None

    ## Manageable properties
    _flagOpenMarket = True
    @property
    def flagOpenMarket(self):
        return self._flagOpenMarket
    @flagOpenMarket.setter
    def flagOpenMarket(self, x):
        self._flagOpenMarket = x

    ## Signals
    # dict as {"names": ["stockName0", "stockName1", ...], "period": period}
    NewData = pyqtSignal(dict)

    def __init__(self):
        QObject.__init__(self)
        self.logger = misc.GetLogger("NavalaSystem")


    def Startup(self):
        """Reinitializes all variables without remorse.

        This should never be called twice. However, when subprocesses are spawned,
        Startup() is called again inside NavalaProcess.run() because the subprocess
        runs under a different System. For example, the database connection in
        subprocesses must never be local.
        """
        
        import tradezone
        import store
        import processmonitor
        import rtclasses

        self.logger.info("Initializing System...")
        try:
            # == Startup sequence ==
            # * Datatabase...
            self.StartDB()
            # * Store...
            self.store = store.Store()
            if self.flagMaster:
                # * Process Monitor...
                self.pm = processmonitor.ProcessMonitor()
                # * Realtime Manager...
                self.rtm = rtclasses.RealtimeManager()
                self.rtm.SetStocksets("Realtime")
                if self.flagStartRTM:
                    self.rtm.Start()

            self.logger.info("Initializing System...OK")
        except:
            self.Cleanup()
            raise

    def StartDB(self):
        import database

        # Database...
        if self.flagMaster:
            db = database.Database()
            db.filename = con.fig.GetRaw("databaseName")
            db.CreateIfDoesNotExist()
            db.AssertOpen()
            self.db = db
            self.logger.info("Spawning DBServer thread...")
            self.dbserver = database.DBServer()
            try: # Note that Starting the DB Server is not considered essential anymore.
                self.dbserver.Start()
                self.logger.info("Spawning DBServer thread...OK")
            except:
                self.logger.exception("Spawning DBServer thread FAILED")
        else:
            self.logger.info("Getting database proxy...")
            db = database.GetDatabaseProxy()
            self.logger.info("Getting database proxy...OK")
            self.db = db
    
    def Cleanup(self):
        """Finishes all processes and threads.
        
        Processes go first, because threads are closer than processes."""
        if self.pm:
            self.pm.ExitAll()
        
        # Threads are currently not exiting in any particular order
        tt = threading.enumerate()
        for item in tt:
            if item and item.is_alive() and hasattr(item, "Exit"):
                self.logger.debug("Exiting thread %s (class %s)..." % 
                              (item.name, item.__class__.__name__))
                item.Exit()
        for item in tt:
            if item and item.is_alive() and hasattr(item, "Exit"):
                item.join()
            
        self.logger.debug("FINISHED CLEANING UP!!!")

    def EmitNewData(self, data):
        """Emits NewData signal."""
        self.NewData.emit(data)


################################################################################
# Creates System instance
vala = System()


